package webpass

import (
    "crypto/rand"
    "crypto/sha256"
    "encoding/base64"
    mrand "math/rand"
    "strconv"
    "strings"
    "time"

    "golang.org/x/crypto/pbkdf2"
)

type pbkdf2Suit struct {
    nIteration    int
    nKeyLen       int
    algorithmName string
    maxRetry      int
    mrand         *mrand.Rand
}

func NewPbkdf2Suit(algorithmName string, nIteration int, nKeylen int) *pbkdf2Suit {
    x := pbkdf2Suit{}
    x.algorithmName = algorithmName
    x.nIteration = nIteration
    x.nKeyLen = nKeylen
    x.mrand = mrand.New(mrand.NewSource(time.Now().UnixNano()))
    x.maxRetry = 32

    return &x
}

func (s *pbkdf2Suit) hash_with_salt(plain_pwd string, salt []byte) string {
    tmp := pbkdf2.Key([]byte(plain_pwd), salt, s.nIteration, s.nKeyLen, sha256.New)
    return base64.StdEncoding.EncodeToString(tmp)
}

func (s *pbkdf2Suit) randBytes(n int) []byte {
    buf := make([]byte, n)

    for i := 0; ; i++ {
        if i > s.maxRetry {
            s.mrand.Read(buf)
            return buf
        }
        if _, err := rand.Read(buf); err != nil {
            continue
        }
        break
    }
    return buf
}

func (s *pbkdf2Suit) MakeCipherText(plain_pwd string) string {
    salt := s.randBytes(16)
    return s.algorithmName + "$" +
        strconv.Itoa(s.nIteration) + "$" +
        base64.StdEncoding.EncodeToString(salt) + "$" +
        s.hash_with_salt(plain_pwd, salt)
}

func (s *pbkdf2Suit) PasswordVerify(plain_pwd string, cipher_pwd string) bool {
    tmp := strings.Split(cipher_pwd, "$")
    if len(tmp) < 4 {
        return false
    }
    if tmp[0] != s.algorithmName {
        return false
    }
    if tmp[1] != strconv.Itoa(s.nIteration) {
        return false
    }
    salt, err := base64.StdEncoding.DecodeString(tmp[2])
    if err != nil {
        return false
    }
    if s.hash_with_salt(plain_pwd, salt) != tmp[3] {
        return false
    }
    return true
}
